#include "publisher.h"
#include <stdio.h>
#include <stdlib.h>

#define REDIS_SERVER_IP "127.0.0.1"
#define REDIS_SERVER_PORT 6379

Publisher::Publisher(const char* channel, const char* message1, const char* message2, int intervalSeconds) :
    m_channel(channel), m_message1(message1), m_message2(message2), m_intervalSeconds(intervalSeconds), m_currentMessageIndex(0) {
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);

    connectToRedis();
    initializeLibevent();
    createPublishEvent();
}

Publisher::~Publisher() {
    event_free(m_publishEvent);
    cleanupLibevent();
    releaseRedisContext();
    WSACleanup();
}

void Publisher::runEventLoop() {
    event_base_dispatch(m_base);
}

void Publisher::publishMessage() {
    const char* messageToSend = (m_currentMessageIndex == 0) ? m_message1.c_str() : m_message2.c_str();
    redisReply* reply = (redisReply*)redisCommand(m_context, "PUBLISH %s %s", m_channel.c_str(), messageToSend);
    if (reply != NULL) {
        printf("Published message: %s\n", messageToSend);
        freeReplyObject(reply);
    }
    else {
        printf("Error publishing message\n");
    }
    // Toggle message index for the next iteration
    m_currentMessageIndex = (m_currentMessageIndex + 1) % 2;
}

void Publisher::connectToRedis() {
    m_context = redisConnect(REDIS_SERVER_IP, REDIS_SERVER_PORT);
    if (m_context == NULL || m_context->err) {
        if (m_context) {
            printf("Error: %s\n", m_context->errstr);
            redisFree(m_context);
        }
        else {
            printf("Unable to allocate redis context\n");
        }
        exit(1);
    }
}

void Publisher::releaseRedisContext() {
    redisFree(m_context);
}

void Publisher::initializeLibevent() {
    m_base = event_base_new();
    if (!m_base) {
        fprintf(stderr, "Error initializing libevent\n");
        exit(1);
    }
}

void Publisher::cleanupLibevent() {
    event_base_free(m_base);
}

void Publisher::publishCallback(evutil_socket_t fd, short events, void* arg) {
    Publisher* publisher = (Publisher*)arg;
    publisher->publishMessage();
    struct timeval tv = { publisher->m_intervalSeconds, 0 };
    evtimer_add(publisher->m_publishEvent, &tv);
}

void Publisher::createPublishEvent() {
    struct timeval tv = { m_intervalSeconds, 0 };
    m_publishEvent = event_new(m_base, -1, EV_PERSIST, publishCallback, this);
    evtimer_add(m_publishEvent, &tv);
}